package com.gs.process.admin.service;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gs.process.admin.context.ProcessContext;
import com.gs.process.core.user.ProcessUserService;
import com.gs.process.model.dto.AppointParam;
import com.gs.process.admin.utils.ProcessUtil;
import com.gs.process.admin.utils.ValidateUtil;
import com.gs.process.model.Approval;
import com.gs.process.model.ApprovalDetails;
import com.gs.process.model.Process;
import com.gs.process.model.ProcessNode;
import com.gs.process.admin.mapper.ApprovalMapper;
import com.gs.process.model.dto.Constant;
import com.gs.process.admin.utils.PageUtil;
import com.shallowUniverse.model.Params;
import com.shallowUniverse.utils.AssertUtil;
import com.shallowUniverse.utils.ObjectBuilder;
import com.shallowUniverse.utils.PrimaryKeyUtil;
import com.shallowUniverse.utils.StreamUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author gs
 * @date create in 2020/12/11 16:34
 */
@Slf4j
@Service
public class ApprovalService extends ServiceImpl<ApprovalMapper, Approval> {

    @Resource
    private ApprovalMapper approvalMapper;
    @Resource
    private ProcessUserService processUserService;
    @Resource
    private ApprovalDetailsService approvalDetailsService;
    @Resource
    private ProcessService processService;
    @Resource
    private ProcessNodeService processNodeService;
    @Resource
    private ProcessContext processContext;

    @Transactional(rollbackFor = Exception.class)
    public boolean save(Approval approval) {
        //设置提交人
        approval.setUserId(processUserService.getUserId());
        //设置当前日期
        approval.setCreateDate(DateUtil.now());
        //拿到流程id字段值
        //通过流程号拿到流程
        Process process = processService.getById(approval.getProcessId());
        //验证是否还存在该流程
        AssertUtil.service().notNull(process, "该流程不存在");
        log.info("拿到流程为：" + process.getName());
        //通过流程号拿到第一个节点
        List<ProcessNode> processNodes = processNodeService.list(
                new QueryWrapper<ProcessNode>().lambda()
                        .eq(ProcessNode::getProcessId, process.getSid())
                        .eq(ProcessNode::getSeq, 1)
        );
        //判断第一个节点是否为空
        AssertUtil.service().notEmpty(processNodes, "当前流程不存在任何节点");

        //设置流程id
        approval.setProcessId(process.getSid());
        //先根据角色和部门查询当前登录用户是否存在审批角色
        List<ProcessNode> nodeByRole = processNodeService.list(
                new QueryWrapper<ProcessNode>().lambda()
                        .eq(ProcessNode::getProcessId, process.getSid())
                        .in(ProcessNode::getRoleId, processUserService.getUserRoles())
                        .and(lambda -> lambda.eq(ProcessNode::getDeptId, processUserService.getUserDept())
                                .or()
                                .isNull(ProcessNode::getDeptId)
                        ).orderByDesc(ProcessNode::getSeq)
        );
        //判断是否为空（不为空证明存在，重新赋值）
        if (!CollectionUtils.isEmpty(nodeByRole)) {
            //先拿到最大的值
            ProcessNode maxProcessNode = nodeByRole.get(0);
            //过滤出来所有最大的顺序并重新复制给当前节点
            processNodes = nodeByRole.stream().filter(
                    processNodeDefinition -> processNodeDefinition.getSeq().equals(maxProcessNode.getSeq())
            ).collect(Collectors.toList());
        }
        //查找下一个节点
        List<ProcessNode> nextNodes = processNodeService.list(
                new QueryWrapper<ProcessNode>().lambda()
                        .eq(ProcessNode::getProcessId, process.getSid())
                        .eq(ProcessNode::getSeq, processNodes.get(0).getSeq() + 1)
        );
        if (CollectionUtils.isEmpty(nextNodes)) {
            //判断角色是否包含在当前流程节点中，包含并且不存在下一个节点直接审批通过
            if (!CollectionUtils.isEmpty(nodeByRole)) {
                approval.setStatus(Constant.PROCESS_ALLOW_SIGN);
            }
            //设置当前节点
            approval.setCurrent(StreamUtil.join(processNodes, ProcessNode::getSid, ","));
        } else {
            //判断是否包含在审批节点列表中则跳到下一个节点
            if (!CollectionUtils.isEmpty(nodeByRole)) {
                approval.setCurrent(StreamUtil.join(nextNodes, ProcessNode::getSid, ","));
            } else {
                //不为0并且不包含在审批节点列表中则保持当前节点
                approval.setCurrent(StreamUtil.join(processNodes, ProcessNode::getSid, ","));
            }
        }

        //判断是否已经设置了状态
        if (ObjectUtils.isEmpty(approval.getStatus())) {
            approval.setStatus(Constant.PROCESS_HANDLE_SIGN);
        }

        log.debug("流程第一个节点为：" + StreamUtil.join(StreamUtil.extract(processNodes, ProcessNode::getName), ","));

        //设置sid
        approval.setSid(PrimaryKeyUtil.generate());
        //保存approval
        int total = approvalMapper.insert(approval);

        return total > 0;
    }

    public IPage<Approval> findByRole(Params<Approval> params) {
        //定义返回对象
        IPage<Approval> page;
        //获取查询需要的参数
        String role = StreamUtil.join(processUserService.getUserRoles(), ",");
        Long deptId = processUserService.getUserDept();
        Long appoint = processUserService.getUserId();
        //判断是否分页
        if (params.isPaging()) {
            page = approvalMapper.selectPageByRole(PageUtil.create(params), role, deptId, appoint);
        } else {
            page = new Page<>();
            List<Approval> approvals = approvalMapper.selectListByRole(role, deptId, appoint);
            page.setRecords(approvals);
            page.setTotal(approvals.size());
        }
        //设置当前节点名称
        ProcessUtil.populateProcessNode(page.getRecords(), processNodeService, this);
        //设置提交人
        ProcessUtil.populateUser(page.getRecords(),processContext);
        //返回
        return page;
    }

    public IPage<Approval> findByUser(Params<Approval> params) {
        //定义返回对象
        IPage<Approval> page;
        //判断是否分页
        if (params.isPaging()) {
            page = approvalMapper.selectPageByUser(PageUtil.create(params), processUserService.getUserId());
        } else {
            page = new Page<>();
            List<Approval> approvals = approvalMapper.selectListByUser(processUserService.getUserId());
            page.setRecords(approvals);
            page.setTotal(approvals.size());
        }
        //设置当前节点名称
        ProcessUtil.populateProcessNode(page.getRecords(), processNodeService, this);
        //设置提交人
        ProcessUtil.populateUser(page.getRecords(),processContext);
        //返回
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean remove(List<Long> ids) {
        //判断是否传了id
        AssertUtil.service().notEmpty(ids, "请传递正确的参数");
        //判断是否存在未完成的审批单
        AssertUtil.service().notEmpty(ValidateUtil.processCompletedBySid(ids, this), "存在未完成的流程");
        //执行删除
        approvalMapper.deleteBatchIds(ids);
        //删除详情
        approvalDetailsService.remove(new UpdateWrapper<ApprovalDetails>().lambda().in(ApprovalDetails::getApprovalId, ids));

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean updateApprovePerson(AppointParam appointParam) {
        //看是否传递了参数
        AssertUtil.service().isTrue(!ObjectUtils.isEmpty(appointParam) && !CollectionUtils.isEmpty(appointParam.getApprovalIds()) && !CollectionUtils.isEmpty(appointParam.getAppointIds()), "请传递正确的参数");
        //定义修改对象
        Approval approval = new Approval();
        approval.setAppoint(StreamUtil.join(appointParam.getAppointIds(), ","));
        //修改
        appointParam.getApprovalIds().forEach(sid -> {
            approval.setSid(sid);
            approvalMapper.updateById(approval);
        });
        return true;
    }

    public IPage<Approval> find(Params<Approval> params) {
        IPage<Approval> page;
        //判断是否分页
        if (params.isPaging()) {
            page = approvalMapper.selectPage();
        } else {
            page = new Page<>();
            List<Approval> approvals = approvalMapper.selectList();
            page.setRecords(approvals);
            page.setTotal(approvals.size());
        }
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNode(page.getRecords(), processNodeService, this);
        //设置提交人
        ProcessUtil.populateUser(page.getRecords(),processContext);
        //返回
        return page;
    }

    public Approval findById(Long id) {
        Approval approval = approvalMapper.selectById(id);
        //封装目标数据和节点
        ProcessUtil.populateProcessNode(
                ObjectBuilder.create(ArrayList<Approval>::new)
                        .with(List::add, approval)
                        .build(),
                processNodeService, this
        );
        //设置提交人
        ProcessUtil.populateUser(Collections.singletonList(approval),processContext);
        return approval;
    }
}
